﻿using System;
using System.Collections.Generic;
using System.Text;

using System.Collections.ObjectModel;

namespace SharedGenomics.Domain
{
    public class SnpCollection : ICollection<SnpDescriptor>
    {
        #region Private Members

        private HashSet<long> removedSnps;
        private KeyedSnpCollection snps;

        #endregion

        #region Constructors

        public SnpCollection()
        {
            this.removedSnps = new HashSet<long>();
            this.snps = new KeyedSnpCollection();
        }

        #endregion

        #region Public Methods

        public bool Restore(long snpId)
        {
            if (this.snps.Contains(snpId) && this.removedSnps.Contains(snpId))
            {
                //snp has been added and then removed
                this.removedSnps.Remove(snpId);
                return true;
            }
            else
                return false;
        }

        public void RestoreAll()
        {
            this.removedSnps.Clear();
        }

        #endregion

        #region Properties

        public SnpDescriptor this[long snpId]
        {
            get
            {
                if (this.Contains(snpId))
                    return this.snps[snpId];
                else
                    throw new ArgumentException(String.Format("No SNP with Id {0} was found in this collection", snpId));
            }
        }

        #endregion

        #region ICollection<SnpDescriptor> Members

        public void Add(SnpDescriptor item)
        {
            long snpId;

            if (item == null)
                throw new ArgumentNullException("item");

            snpId = item.Id;

            if (this.snps.Contains(snpId))
            {
                if (this.removedSnps.Contains(snpId))
                {
                    //remove the old item from the keyed snp collection, before adding the new one and un-deleting the key
                    this.snps.Remove(snpId);
                    this.snps.Add(item);
                    this.removedSnps.Remove(snpId);
                }
                else
                    throw new ArgumentException(String.Format("A SNP with Id {0} has already been added to this collection", snpId));
            }
            else
            {
                this.snps.Add(item);
            }
        }

        public void Clear()
        {
            foreach (SnpDescriptor snp in this.snps)
            {
                this.removedSnps.Add(snp.Id);
            }
        }

        public bool Contains(SnpDescriptor item)
        {
            return (this.snps.Contains(item) && !this.removedSnps.Contains(item.Id));
        }

        public bool Contains(long snpId)
        {
            return (this.snps.Contains(snpId) && !this.removedSnps.Contains(snpId));
        }

        public void CopyTo(SnpDescriptor[] array, int arrayIndex)
        {
            if (array == null)
                throw new ArgumentNullException("array");

            if (arrayIndex < 0)
                throw new ArgumentOutOfRangeException("arrayIndex");

            if (array.Rank > 1 ||
                arrayIndex >= array.Length ||
                arrayIndex + this.Count > array.Length)
                throw new ArgumentException();

            foreach (SnpDescriptor sd in this)
                array[arrayIndex++] = sd;
        }

        public int Count
        {
            get { return this.snps.Count - this.removedSnps.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(SnpDescriptor item)
        {
            return (item != null && this.Remove(item.Id));
        }

        public bool Remove(long snpId)
        {
            if (this.snps.Contains(snpId) && !this.removedSnps.Contains(snpId))
            {
                this.removedSnps.Add(snpId);
                return true;
            }
            else
                return false;
        }

        #endregion

        #region IEnumerable<SnpDescriptor> Members

        public IEnumerator<SnpDescriptor> GetEnumerator()
        {
            foreach (SnpDescriptor sd in this.snps)
            {
                if (!this.removedSnps.Contains(sd.Id))
                    yield return sd;
            }
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion

        #region KeyedSnpCollection

        private class KeyedSnpCollection : KeyedCollection<long, SnpDescriptor>
        {
            protected override long GetKeyForItem(SnpDescriptor item)
            {
                return item.Id;
            }
        }

        #endregion
    }
}
